package com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.dp.mndataif003;

import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.dp.DomUtil;
import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.dp.ErrorCodeConstant;
import com.alibaba.cirtus.ots.application.adapter.ability.model.sdo.CustomerAddressSDO;
import com.alibaba.cirtus.ots.application.adapter.ability.model.sdo.CustomerSDO;
import com.alibaba.cirtus.ots.application.adapter.ability.model.sdo.CustomerSaleScopeSDO;
import com.alibaba.citrus.ots.common.annotation.ProcessInvoker;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.bcorder.model.dto.OrganizationSDO;
import com.epoch.app.crplatformenhance.domain.customer.model.Customer;
import com.epoch.app.otsapplicationadaptersdk.contants.OtsApplicationAdapterSdkConstants;
import com.epoch.app.otsapplicationadaptersdk.dp.DpDelivery;
import com.epoch.app.otsapplicationadaptersdk.dp.MnDataDeliveryResponse;
import com.epoch.app.otsapplicationadaptersdk.dp.MnDataDeliverySDO;
import com.epoch.app.otsapplicationadaptersdk.dp.mndataif003electricity.dto.BatchDoHandlerRequest;
import com.epoch.app.otsapplicationadaptersdk.dp.mndataif003electricity.dto.DoHandlerRequest;
import com.epoch.app.otsapplicationadaptersdk.dp.mndataif003electricity.service.MnDataIf003ElectricityService;
import com.epoch.app.otsapplicationadaptersdk.model.dto.RestDpDeliveryDataElectricityRequest;
import com.epoch.app.otsapplicationadaptersdk.service.DpService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author wangyibing
 * @description: 送达方电商同步
 * @date 2021/9/25 1:08
 */
@Component
public class MnDataIf003ElectricityV2ServiceImpl implements MnDataIf003ElectricityService {
    Log log = Log.getLogger(MnDataIf003ElectricityV2ServiceImpl.class);
    @Resource
    private MnDataIf003SDOBuilderV2 mnDataIf003SDOBuilder;

    @Resource
    private MnDataIf003SyncUtilsV2 mnDataIf003SyncUtils;

    @Resource
    private DpService dpService;

    @ProcessInvoker(remark = "batchMnDataIf003ElectricityService", errorCode = "", printLog = true)
    @Override
    public Object batchDoHandler(BatchDoHandlerRequest batchDoHandlerRequest) {
        if(batchDoHandlerRequest.getPullDays() == null || batchDoHandlerRequest.getPullDays() <= 0){
            return Result.fail("","入参pullDays错误");
        }
        String beginDate = batchDoHandlerRequest.getDs();
        for(int i = 1; i<=batchDoHandlerRequest.getPullDays(); i++){
            DoHandlerRequest doHandlerRequest = new DoHandlerRequest();
            doHandlerRequest.setFromSys(batchDoHandlerRequest.getFromSys());
            doHandlerRequest.setPageNum(batchDoHandlerRequest.getPageNum());
            doHandlerRequest.setPageSize(batchDoHandlerRequest.getPageSize());
            doHandlerRequest.setDs(beginDate);
            Object object = doHandler(doHandlerRequest);
            beginDate = DomUtil.getRollbackDate(DomUtil.parse(beginDate),1);
        }
        return Result.success(null);
    }

    @ProcessInvoker(remark = "MnDataIf003ElectricityService", errorCode = "", printLog = true)
    @Override
    public MnDataDeliveryResponse doHandler(DoHandlerRequest doHandlerRequest) {
        // 1、构造中台查询参数
        RestDpDeliveryDataElectricityRequest param = buildMnQueryMap(doHandlerRequest);
        // 调用中台接口获取客户主数据
        MnDataDeliveryResponse mnDataDeliveryResponse = getMnDataDeliveryResponse(param);
        MnDataDeliverySDO data = mnDataDeliveryResponse.getData();
        if (Objects.isNull(data) || CollectionUtils.isEmpty(data.getRows())) {
            //log.info("MnDataIf003ElectricityService_pull_data_result_is_null,query_params={}", JSONObject.toJSON(param));
            return mnDataDeliveryResponse;
        }
        // 总页数
        int totalPage = getTotalPage(data);
        log.info("MnDataIf003ElectricityService_total={},page={},query_params={}", data.getTotalNum(), totalPage, JSONObject.toJSON(param));

        long before = System.currentTimeMillis();
        // 2、分页同步客户主数据
        for (int current = 1; current <= totalPage; current++) {
            log.info("电商送达方同步开始，开始同步第{}页", current);
            log.info("MnDataIf003ElectricityService_doHandler_page_start,current_page={}", current);
            param.setPageNum(String.valueOf(current));
            long start = System.currentTimeMillis();
            MnDataDeliveryResponse addressRes = getMnDataDeliveryResponse(param);
            log.info("MnDataIf003ElectricityService_doHandler_rest_getMnDataDeliveryResponse={}", JSONObject.toJSONString(addressRes));
            if (Objects.isNull(addressRes.getData()) || CollectionUtils.isEmpty(addressRes.getData().getRows())) {
                continue;
            }
            List<DpDelivery> dpDeliveries = addressRes.getData().getRows();
            try {
                syncCurrentPageOfCustomerAddress(dpDeliveries);
            } catch (Exception e){
                log.error("MnDataIf003ElectricityService_doHandler_exception_current_page_error ", e);
                log.error("MnDataIf003ElectricityService_doHandler_exception_current_page={},query_params={}", current,JSONObject.toJSON(param));
            }
            log.info("MnDataIf003ElectricityService_doHandler_page_end,current_page={},elapsed_time={}", current, System.currentTimeMillis() - start);
        }
        log.info("MnDataIf003ElectricityService_doHandler_end,elapsed_time={}", System.currentTimeMillis() - before);
        return mnDataDeliveryResponse;
    }

    @NotNull
    private MnDataDeliveryResponse getMnDataDeliveryResponse(RestDpDeliveryDataElectricityRequest param) {
        MnDataDeliveryResponse mnDataDeliveryResponse = null;
        int retryTimes = 0;
        String errCode = null;
        do {
            if (ErrorCodeConstant.RATE_LIMIT.equals(errCode)) {
                try {
                    Thread.sleep(120000);
                } catch (InterruptedException e) {
                    log.error("MnDataIf003ElectricityServiceImpl#doHandler InterruptedException", e);
                }
            }
            try {
                mnDataDeliveryResponse = dpService.restDpDeliveryDataElectricity(param);
            } catch (Exception e) {
                log.error("MnData_rest_req_occur_exception,restDpDeliveryDataElectricity_errorMsg={}",e.getMessage());
                throw e;
            }
            errCode = mnDataDeliveryResponse.getErrCode();
        } while (!"0".equals(errCode) && retryTimes++ < 10);
        return mnDataDeliveryResponse;
    }

    public int getTotalPage(MnDataDeliverySDO data) {
        Integer totalNum = data.getTotalNum();
        Integer pageSize = data.getPageSize();
        int totalPage = 1;
        if (totalNum % pageSize == 0) {
            totalPage = totalNum / pageSize;
        } else {
            totalPage = (totalNum / pageSize) + 1;
        }
        return totalPage;
    }

    private void syncCurrentPageOfCustomerAddress(List<DpDelivery> mnCustomerAddress) {
        //拉取数据同步销售组织过滤配置
        if (StringUtils.isNotBlank(OtsApplicationAdapterSdkConstants.pull_customer_data_organization())
                && !"-1".equals(OtsApplicationAdapterSdkConstants.pull_customer_data_organization())) {

            List filterOrgList = Arrays.stream(OtsApplicationAdapterSdkConstants.pull_customer_data_organization().split(",")).collect(Collectors.toList());
            mnCustomerAddress = mnCustomerAddress.stream().filter(x->filterOrgList.contains(x.getVkorg())).collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(mnCustomerAddress)) {
            log.info("syncCurrentPageOfCustomerAddress_filterOrgData_mnElectricityAddress_is_null, filterOrgList={}",OtsApplicationAdapterSdkConstants.pull_customer_data_organization());
            return;
        }

        Map<String, OrganizationSDO> organizationMapByCustomers = mnDataIf003SDOBuilder.getOrganizationMapByAddresses(mnCustomerAddress);
        log.info("syncCurrentPageOfCustomerAddress,request,mnCustomerAddress:{}",mnCustomerAddress);
        log.info("syncCurrentPageOfCustomerAddress,response,organizationMapByCustomers:{}",organizationMapByCustomers);
        if (Objects.isNull(organizationMapByCustomers)) {
            return;
        }

        List<CustomerSDO> customerSDOs = Lists.newArrayList();
        List<CustomerSaleScopeSDO> customerSaleScopeSDOs = Lists.newArrayList();
        List<CustomerAddressSDO> customerAddressSDOs = Lists.newArrayList();
        for (DpDelivery address : mnCustomerAddress) {
            // 客户组编码是0001，且客户编码和送达方编码一致，需要先生成客户、销售范围
            if ("0001".equals(address.getKtokd()) && Objects.equals(address.getKunn2(), address.getKunnr())) {
                CustomerSDO customerSDO = mnDataIf003SDOBuilder.buildCustomerMainData(address, organizationMapByCustomers);
                if (Objects.isNull(customerSDO)) {
                    continue;
                }
                customerSDOs.add(customerSDO);
                CustomerSaleScopeSDO saleScopeSDO = mnDataIf003SDOBuilder.buildCustomerSaleScopeSDO(address, organizationMapByCustomers);
                if (Objects.isNull(saleScopeSDO)) {
                    continue;
                }
                customerSaleScopeSDOs.add(saleScopeSDO);
            }
            CustomerAddressSDO customerAddressSDO = mnDataIf003SDOBuilder.buildCustomerAddressSDO(address, organizationMapByCustomers);
            log.info("003electricity_syncCurrentPageOfCustomerAddress_buildCustomerAddressSDO_result={},pull_origin_data={}", JSONObject.toJSON(customerAddressSDO), JSONObject.toJSON(address));
            if (Objects.isNull(customerAddressSDO)) {
                continue;
            }
            customerAddressSDOs.add(customerAddressSDO);
        }
        // 批量新增或更新客户主档数据
        batchSaveAndUpdateCustomers(customerSDOs);
        // 批量新增或更新客户销售范围
        batchSaveAndUpdateCustomerScopes(customerSaleScopeSDOs);
        // 批量新增或更新客户收货地址
        batchSaveAndUpdateAddresses(customerAddressSDOs);
    }

    private void batchSaveAndUpdateAddresses(List<CustomerAddressSDO> customerAddressSDOs) {
        if (CollectionUtils.isEmpty(customerAddressSDOs)) {
            return;
        }
        List<CustomerAddressSDO> existAddressList = mnDataIf003SyncUtils.batchQueryCustomerAddress(customerAddressSDOs);
        Map<String, CustomerAddressSDO> existAddressMap = existAddressList.stream().collect(Collectors.toMap((it -> mnDataIf003SyncUtils.getAddressCombineKey(it)), a -> a, (a, b) -> a));
        List<CustomerAddressSDO> notExistAddresses = customerAddressSDOs.stream().filter(it -> !existAddressMap.containsKey(mnDataIf003SyncUtils.getAddressCombineKey(it))).collect(Collectors.toList());
        Map<String, CustomerAddressSDO> addressUpdatedMap = customerAddressSDOs.stream().filter(it -> existAddressMap.containsKey(mnDataIf003SyncUtils.getAddressCombineKey(it))).collect(Collectors.toMap(it -> mnDataIf003SyncUtils.getAddressCombineKey(it), a -> a, (a, b) -> a));
        Boolean addressSave = mnDataIf003SyncUtils.batchCreateCustomerAddresses(notExistAddresses);
        Boolean addressUpdated = mnDataIf003SyncUtils.batchUpdateCustomerAddresses(existAddressList, addressUpdatedMap);
    }

    private void batchSaveAndUpdateCustomerScopes(List<CustomerSaleScopeSDO> customerSaleScopeSDOs) {
        if (CollectionUtils.isEmpty(customerSaleScopeSDOs)) {
            return;
        }
        List<CustomerSaleScopeSDO> existScopeList = mnDataIf003SyncUtils.batchQueryCustomerScopes(customerSaleScopeSDOs);
        Map<String, CustomerSaleScopeSDO> existScopeMap = existScopeList.stream().collect(Collectors.toMap((it -> mnDataIf003SyncUtils.getScopeCombineKey(it)), a -> a, (a, b) -> a));
        List<CustomerSaleScopeSDO> notExistScopes = customerSaleScopeSDOs.stream().filter(it -> !existScopeMap.containsKey(mnDataIf003SyncUtils.getScopeCombineKey(it))).collect(Collectors.toList());
        Map<String, CustomerSaleScopeSDO> updatedMap = customerSaleScopeSDOs.stream().filter(it -> existScopeMap.containsKey(mnDataIf003SyncUtils.getScopeCombineKey(it))).collect(Collectors.toMap(it -> mnDataIf003SyncUtils.getScopeCombineKey(it), a -> a, (a, b) -> a));
        Boolean scopeSave = mnDataIf003SyncUtils.batchCreateCustomerScopes(notExistScopes);
        Boolean scopeUpdate = mnDataIf003SyncUtils.batchUpdateCustomerScopes(existScopeList, updatedMap);
    }

    private void batchSaveAndUpdateCustomers(List<CustomerSDO> customerSDOs) {
        if (CollectionUtils.isEmpty(customerSDOs)) {
            return;
        }
        Set<String> customerCodeSet = customerSDOs.stream().map(CustomerSDO::getCode).collect(Collectors.toSet());
        List<String> customerCodes = new ArrayList<>(customerCodeSet);
        List<Customer> existCustomers = mnDataIf003SyncUtils.batchQueryCustomerMainData(customerCodes);
        Map<String, Customer> existCustomerMap = existCustomers.stream().collect(Collectors.toMap(Customer::getCode, a -> a, (a, b) -> a));
        Map<String, CustomerSDO> updateMap = customerSDOs.stream().filter(it -> existCustomerMap.containsKey(it.getCode())).collect(Collectors.toMap(CustomerSDO::getCode, a -> a, (a, b) -> a));
        List<CustomerSDO> notExistCustomers = customerSDOs.stream().filter(it -> !existCustomerMap.containsKey(it.getCode())).collect(Collectors.toList());
        //处理新增的客户
        Boolean save = mnDataIf003SyncUtils.batchCreateCustomers(notExistCustomers);
        Boolean update = mnDataIf003SyncUtils.batchUpdateCustomers(existCustomers, updateMap);
    }

    private RestDpDeliveryDataElectricityRequest buildMnQueryMap(DoHandlerRequest doHandlerRequest) {
        RestDpDeliveryDataElectricityRequest dataGroupRequest = RestDpDeliveryDataElectricityRequest.builder()
                .fromSys(doHandlerRequest.getFromSys())
                .pageNum(doHandlerRequest.getPageNum())
                .pageSize(doHandlerRequest.getPageSize())
                .ds(doHandlerRequest.getDs())
                .build();
        return dataGroupRequest;
    }
}
